import { defineComponent } from "@/shared/define.js";

/**
 * @typedef {{ name: string, location: string } } HeroType
 */
class HeroListController {
  static $inject = ["$scope", "$element", "$attrs"];

  /**
   * @param {ng.IScope} $scope
   * @param {Element} $element
   * @param {ng.IAttributes} $attrs
   */
  constructor($scope, $element, $attrs) {
    this.$scope = $scope;
    this.$element = $element;
    this.$attrs = $attrs;
  }

  list = [
    {
      name: "Superman",
      location: "",
    },
    {
      name: "Batman",
      location: "Wayne Manor",
    },
  ];

  /**
   * @param {HeroType} hero
   * @param {'name' | 'location'} prop
   * @param {string} value
   */
  updateHero(hero, prop, value) {
    hero[prop] = value;
  }

  /**
   * @param {HeroType} hero
   */
  deleteHero(hero) {
    const idx = this.list.indexOf(hero);
    if (idx >= 0) {
      this.list.splice(idx, 1);
    }
  }
}

export const HeroList = defineComponent({
  template: `
    <b>Heroes</b>
    <br>
    <hero-detail ng-repeat="hero in $ctrl.list" hero="hero" 
      on-delete="$ctrl.deleteHero(hero)" 
      on-update="$ctrl.updateHero(hero, prop, value)">
    </hero-detail>
  `,
  controller: HeroListController,
});

class HeroDetailController {
  /**
   * @type {string[]}
   */
  static $inject = [];

  constructor() {
    /**
     * @type {{({hero , prop, value } : {hero: HeroType, prop: string, value: string }): void}}
     */
    this.onUpdate;
    /**
     * @type {{(theHero:{hero: HeroType}): void }}
     */
    this.onDelete;
    /**
     * @type {HeroType}
     */
    this.hero;
  }

  delete() {
    this.onDelete({ hero: this.hero });
  }

  /**
   * @param {string} prop
   * @param {string} value
   */
  update(prop, value) {
    this.onUpdate({ hero: this.hero, prop, value });
  }
}

export const HeroDetail = defineComponent({
  template: `
  <hr>
  <div>
    Name: {{$ctrl.hero.name}}<br>
    Location: <editable-field field-value="$ctrl.hero.location" field-type="text" on-update="$ctrl.update('location', value)"></editable-field><br>
    <button ng-click="$ctrl.delete()">Delete</button>
  </div>
  `,
  controller: HeroDetailController,
  bindings: {
    hero: "<",
    onDelete: "&",
    onUpdate: "&",
  },
});

class EditableFieldController {
  static $inject = ["$scope", "$element", "$attrs"];

  /**
   * @param {ng.IScope} $scope
   * @param {Element} $element
   * @param {ng.IAttributes} $attrs
   */
  constructor($scope, $element, $attrs) {
    this.$scope = $scope;
    this.$element = $element;
    this.$attrs = $attrs;
    this.editMode = false;
    /**
     * @type {{({ value } : { value: HeroType }): void}}
     */
    this.onUpdate;
    /**
     * @type {HeroType}
     */
    this.fieldValue;
    /**
     * @type {HeroType}
     */
    this.fieldValueCopy;
  }

  handleModeChange() {
    if (this.editMode) {
      this.onUpdate({ value: this.fieldValue });
      this.fieldValueCopy = this.fieldValue;
    }
    this.editMode = !this.editMode;
  }

  reset() {
    this.fieldValue = this.fieldValueCopy;
  }

  $onInit() {
    // Make a copy of the initial value to be able to reset it later
    this.fieldValueCopy = this.fieldValue;

    // Set a default fieldType
    if (!this.fieldType) {
      this.fieldType = "text";
    }
  }
}

export const EditableField = defineComponent({
  template: `
    <span ng-switch="$ctrl.editMode">
      <input ng-switch-when="true" type="{{$ctrl.fieldType}}" ng-model="$ctrl.fieldValue">
      <span ng-switch-default>{{$ctrl.fieldValue}}</span>
    </span>
    <button ng-click="$ctrl.handleModeChange()">{{$ctrl.editMode ? 'Save' : 'Edit'}}</button>
    <button ng-if="$ctrl.editMode" ng-click="$ctrl.reset()">Reset</button>
  `,
  controller: EditableFieldController,
  bindings: {
    fieldValue: "<",
    fieldType: "@?",
    onUpdate: "&",
  },
});
